package org.dromara.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ijpay.core.kit.WxPayKit;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.config.SystemInfoConfig;
import org.dromara.system.constant.MachineConstant;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.RenewSimBo;
import org.dromara.system.domain.bo.SimRenewRecordsBo;
import org.dromara.system.domain.bo.SimUserPurseBo;
import org.dromara.system.domain.vo.QueryCustomerBalanceVo;
import org.dromara.system.domain.vo.SimRenewRecordsVo;
import org.dromara.system.domain.vo.SingleIccIdInfoReqVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.enums.PayTypeEnum;
import org.dromara.system.enums.RenewStatusEnum;
import org.dromara.system.enums.SysUserProfitRecordsEnum;
import org.dromara.system.mapper.SimRenewRecordsMapper;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.*;
import org.dromara.system.service.impl.pay.AbstractPayService;
import org.dromara.system.service.impl.permission.PermissionBaseService;
import org.dromara.system.util.JsonUtil;
import org.dromara.system.util.OrderUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 续期记录Service业务层处理
 *
 * @author Lion Li
 * @date 2025-08-27
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SimRenewRecordsServiceImpl extends ServiceImpl<SimRenewRecordsMapper, SimRenewRecords>
    implements ISimRenewRecordsService {

    private final String QUERY_CUSTOMER_BALANCE_URL = "https://apizs.eptok.com/api/queryCustomerBalance";
    private final String NUMBER_RENEW_CHECK = "https://apizs.eptok.com/hz/numberRenewCheck";
    private final String RENEW_URL = "https://apizs.eptok.com/hz/numberRenew";
    private final ISimCardService simCardService;
    private final SystemInfoConfig systemInfoConfig;
    private final ISysUserService sysUserService;
    private final PermissionBaseService permissionBaseService;
    private final SysUserMapper sysUserMapper;
    private final ISimUserLevelService simUserLevelService;
    private final ISimUserPurseRecordsService simUserPurseRecordsService;
    private final ISimRenewProfitRecordsService simRenewProfitRecordsService;


    @Resource
    private Map<String, AbstractPayService> abstractPayServiceMap;

    public static void main(String[] args) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");
        String formattedDate = sdf.format(DateUtil.date());
        String token = SecureUtil.md5("18707156547".concat(formattedDate));
        String random = RandomUtil.randomString(9);
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("Account", "18707156547");
        hashMap.put("Password", SecureUtil.md5("1234qwer@"));
        hashMap.put("version", "V2");
        hashMap.put("Token", token);
        hashMap.put("ICCID", "89860470192290249793");
        hashMap.put("OrderNumber", random);
        hashMap.put("Name", "武汉智寰信息");
        String post = HttpUtil.get("https://apizs.eptok.com/hz/numberRenew", hashMap);
        System.out.println(post);
        SingleIccIdInfoReqVo singleIccIdInfoReqVo = JSONUtil.toBean(post, SingleIccIdInfoReqVo.class);

//        String post = HttpUtil.get("https://apizs.eptok.com/api/queryCustomerBalance?23110402556749");
//        System.out.println(post);

    }

    /**
     * 查询续期记录
     *
     * @param id 主键
     * @return 续期记录
     */
    @Override
    public SimRenewRecordsVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询续期记录列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 续期记录分页列表
     */
    @Override
    public TableDataInfo<SimRenewRecordsVo> queryPageList(SimRenewRecordsBo bo, PageQuery pageQuery) {
        IPage<SimRenewRecordsVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        IPage<SimRenewRecordsVo> list = baseMapper.findAll(page, bo);
        return TableDataInfo.build(list);
    }

    /**
     * 查询符合条件的续期记录列表
     *
     * @param bo 查询条件
     * @return 续期记录列表
     */
    @Override
    public List<SimRenewRecordsVo> queryList(SimRenewRecordsBo bo) {
        LambdaQueryWrapper<SimRenewRecords> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SimRenewRecords> buildQueryWrapper(SimRenewRecordsBo bo) {
        LambdaQueryWrapper<SimRenewRecords> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(SimRenewRecords::getId);
        lqw.eq(bo.getUserId() != null, SimRenewRecords::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getIccId()), SimRenewRecords::getIccId, bo.getIccId());
        lqw.eq(bo.getStatus() != null, SimRenewRecords::getStatus, bo.getStatus());
        lqw.eq(bo.getAmount() != null, SimRenewRecords::getAmount, bo.getAmount());
        return lqw;
    }

    /**
     * 新增续期记录
     *
     * @param bo 续期记录
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(SimRenewRecordsBo bo) {
        SimRenewRecords add = MapstructUtils.convert(bo, SimRenewRecords.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改续期记录
     *
     * @param bo 续期记录
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(SimRenewRecordsBo bo) {
        SimRenewRecords update = MapstructUtils.convert(bo, SimRenewRecords.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SimRenewRecords entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除续期记录信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public String renewSim(RenewSimBo bo) {
        StringBuilder errorMsg = new StringBuilder();
        if (ObjectUtil.isNull(bo.getUserId())) {
            bo.setUserId(LoginHelper.getUserId());
        }
        //校验下订单金额是否正确
        BigDecimal userRenewCost = sysUserService.queryUserRenewCost(bo.getUserId());
        BusinessException.isNull(userRenewCost, "上级未设置续期成本");
        int size = bo.getIccIds().size();
        BigDecimal mul = NumberUtil.mul(userRenewCost, Convert.toBigDecimal(size));
        bo.setAmount(mul);
//        BusinessException.isTrue(!NumberUtil.equals(mul, bo.getAmount()), "订单金额异常！");
        //校验集团余额
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("customer_id", systemInfoConfig.getCustomerId());
        String post = HttpUtil.get(QUERY_CUSTOMER_BALANCE_URL, hashMap);
        SingleIccIdInfoReqVo singleIccIdInfoReqVo = JSONUtil.toBean(post, SingleIccIdInfoReqVo.class);
        BusinessException.isTrue(!StrUtil.equals(singleIccIdInfoReqVo.getResultCode(), "0"), "查询集团余额失败！");
        QueryCustomerBalanceVo queryCustomerBalanceVo = JSONUtil.toBean(singleIccIdInfoReqVo.getResultData(), QueryCustomerBalanceVo.class);
        BigDecimal balance = queryCustomerBalanceVo.getBalance();
        //转换为元
        BigDecimal realBalance = NumberUtil.div(balance, MachineConstant.NUMBER.ONE_HUNDRED);
        //计算续期实际成本
        BigDecimal renewalCost = NumberUtil.mul(systemInfoConfig.getRenewalCost(), Convert.toBigDecimal(size));
        BusinessException.isTrue(NumberUtil.isLess(realBalance, renewalCost), "集团余额不足");
        List<String> iccIds = bo.getIccIds();
        iccIds.forEach(t -> {
            HashMap<String, Object> checkHashMap = new HashMap<>();
            checkHashMap.put("ICCID", t);
            checkHashMap.put("OrderNumber", RandomUtil.randomString(10));
            String checkPost = HttpUtil.get(NUMBER_RENEW_CHECK, checkHashMap);
            SingleIccIdInfoReqVo checkPostInfo = JSONUtil.toBean(checkPost, SingleIccIdInfoReqVo.class);
            if (!StrUtil.equals(checkPostInfo.getResultCode(), "0")) {
                errorMsg.append("<br/>").append(t).append(":").append(checkPostInfo.getResultDesc());
            }
        });
        BusinessException.isTrue(StrUtil.isNotBlank(errorMsg), errorMsg.toString());
        String orderNo = OrderUtil.getOrderNo("XQ", 6);
        bo.setOrderNo(orderNo);
        //增加续期记录
        List<SimCard> list = simCardService.lambdaQuery().in(SimCard::getIccId, iccIds).list();
        list.forEach(t -> {
            SimRenewRecordsBo simRenewRecordsBo = SimRenewRecordsBo.builder()
                .iccId(t.getIccId())
                .simCardId(t.getId())
                .no(orderNo)
                .amount(mul)
                .renewAmount(userRenewCost)
                .status(RenewStatusEnum.UNPAID.getCode())
                .userId(bo.getUserId())
                .build();
            insertByBo(simRenewRecordsBo);
        });
        //拉起支付
        AbstractPayService abstractCashBack = abstractPayServiceMap.get(
            PayTypeEnum.findOne(bo.getPayType()).getServiceName());
        return abstractCashBack.initPayReNewOrder(bo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String renewSimWxCallBack(HttpServletRequest request, HttpServletResponse response) {
        try {
            String xmlMsg = readData(request);
            log.info("续期回调返回{}", JsonUtil.toJSONString(xmlMsg));
            Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
            log.info("续期回调返回转json{}", JsonUtil.toJSONString(params));
            String returnCode = params.get("return_code");
            if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(returnCode)) {
                //支付成功
                String orderNo = Convert.toStr(params.get("attach"));
                List<SimRenewRecords> list = lambdaQuery().in(SimRenewRecords::getNo, orderNo).list();
                boolean allStatusZero = list.stream()
                    .allMatch(t -> ObjectUtil.equal(t.getStatus(), RenewStatusEnum.UNPAID.getCode()));
                if (allStatusZero) {
                    list.forEach(t -> {
                        SimCard simCard = simCardService.lambdaQuery().eq(SimCard::getIccId, t.getIccId()).one();
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH");
                        String formattedDate = sdf.format(DateUtil.date());
                        String token = SecureUtil.md5(systemInfoConfig.getAccount().concat(formattedDate));
                        String random = RandomUtil.randomString(9);
                        HashMap<String, Object> hashMap = new HashMap<>();
                        hashMap.put("Account", systemInfoConfig.getAccount());
                        hashMap.put("Password", SecureUtil.md5(systemInfoConfig.getPassword()));
                        hashMap.put("version", "V2");
                        hashMap.put("Token", token);
                        hashMap.put("ICCID", t.getIccId());
                        hashMap.put("OrderNumber", random);
                        hashMap.put("Name", systemInfoConfig.getName());
                        String post = HttpUtil.get(RENEW_URL, hashMap);
                        SingleIccIdInfoReqVo singleIccIdInfoReqVo = JSONUtil.toBean(post, SingleIccIdInfoReqVo.class);
                        t.setStatus(StrUtil.equals(singleIccIdInfoReqVo.getResultCode(), "0")
                            ? RenewStatusEnum.RENEW_SUCCESS.getCode() : RenewStatusEnum.RENEW_FAIL.getCode());
                        t.setRemark(singleIccIdInfoReqVo.getResultDesc());
                        updateById(t);
                        if (ObjectUtil.equal(t.getStatus(), RenewStatusEnum.RENEW_SUCCESS.getCode())) {
                            //成功则执行分润
                            dealProfitWork(t);
                            simCardService.queryIccIdInfo(t.getIccId());
                            //增加已续期次数
                            simCardService.lambdaUpdate()
                                .set(SimCard::getAlreadyRenewNum, simCard.getAlreadyRenewNum() + 1)
                                .eq(SimCard::getIccId, t.getIccId())
                                .update();
                            // 减少待续期次数
                            if (ObjectUtil.equal(t.getUserId(), MachineConstant.ADMIN)
                                && simCard.getRenewNum() > 0) {
                                simCardService.lambdaUpdate()
                                    .set(SimCard::getRenewNum, simCard.getRenewNum() - 1)
                                    .eq(SimCard::getIccId, t.getIccId())
                                    .update();
                            }
                        }
                    });
                }
                Map<String, String> xml = new HashMap<String, String>(2);
                xml.put("return_code", "SUCCESS");
                xml.put("return_msg", "OK");
                return WxPayKit.toXml(xml);
            }
        } catch (Exception e) {
            log.info("微信支付回调失败：{}", e.toString());
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class)
    public void dealProfitWork(SimRenewRecords simRenewRecords) {
        //执行续期分润   备注 上级的等级一定会比下级的等级高
        SysUser sysUser = sysUserMapper.upUser(simRenewRecords.getUserId());
        List<SimUserPurseBo> simUserPurseBos = new ArrayList<>();
        ArrayList<SimRenewProfitRecords> arrayList = Lists.newArrayList();
        AtomicReference<BigDecimal> alreadyProfit = new AtomicReference<>(BigDecimal.ZERO);
        //本级分润
        SysUserVo sysUserVo = sysUserService.selectUserById(simRenewRecords.getUserId());
        if (!ObjectUtil.equal(sysUserVo.getUserId(), MachineConstant.ADMIN)) {
            //下级代理续期
            SimUserLevel simUserLevel = simUserLevelService.lambdaQuery().eq(SimUserLevel::getLevelNumber, sysUserVo.getLevel()).one();
            BigDecimal levelRate = simUserLevel.getLevelRate();
            //本单利润
            BigDecimal taskProfit = NumberUtil.sub(simRenewRecords.getRenewAmount(), systemInfoConfig.getRenewalCost());
            BigDecimal currentProfit = NumberUtil.mul(taskProfit, levelRate);
            SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
                .userId(sysUser.getUserId())
                .type(SysUserProfitRecordsEnum.RENEW.getCode())
                .profit(currentProfit)
                .build();
            simUserPurseBos.add(sysUserPurseBo);
            alreadyProfit.set(currentProfit);
            //分润记录
            SimRenewProfitRecords simRenewProfitRecords = SimRenewProfitRecords.builder()
                .userId(sysUser.getUserId())
                .profit(currentProfit)
                .build();
            arrayList.add(simRenewProfitRecords);
            profitDifference(taskProfit, sysUser.getChildrenSysUser(), currentProfit, alreadyProfit, simUserPurseBos, arrayList);
        } else {
            //admin续期
            SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
                .userId(sysUser.getUserId())
                .type(SysUserProfitRecordsEnum.RENEW.getCode())
                .profit(NumberUtil.sub(simRenewRecords.getRenewAmount(), systemInfoConfig.getRenewalCost()))
                .build();
            simUserPurseBos.add(sysUserPurseBo);
            SimRenewProfitRecords simRenewProfitRecords = SimRenewProfitRecords.builder()
                .userId(sysUser.getUserId())
                .profit(NumberUtil.sub(simRenewRecords.getRenewAmount(), systemInfoConfig.getRenewalCost()))
                .build();
            arrayList.add(simRenewProfitRecords);
        }
        if (CollUtil.isNotEmpty(arrayList)) {
            arrayList.forEach(t -> {
                t.setNo(simRenewRecords.getNo());
                t.setSimCardId(simRenewRecords.getSimCardId());
                t.setIccId(simRenewRecords.getIccId());
                t.setRenewRecordId(simRenewRecords.getId());
            });
            log.info("simRenewProfitRecordsList={}", JSONUtil.toJsonStr(simUserPurseBos));
            simRenewProfitRecordsService.saveBatch(arrayList);
        }
        if (CollUtil.isNotEmpty(simUserPurseBos)) {
            log.info("simUserPurseBos={}", JSONUtil.toJsonStr(simUserPurseBos));
            simUserPurseRecordsService.updateUserPurse(simUserPurseBos);
        }
    }

    public void profitDifference(BigDecimal taskProfit
        , SysUser sysUser, BigDecimal initProfit, AtomicReference<BigDecimal> alreadyProfit, List<SimUserPurseBo> simUserPurseBos, ArrayList<SimRenewProfitRecords> arrayList) {
        if (ObjectUtil.equal(sysUser.getUserId(), MachineConstant.ADMIN)) {
            //admin
            SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
                .userId(sysUser.getUserId())
                .type(SysUserProfitRecordsEnum.RENEW.getCode())
                .profit(NumberUtil.sub(taskProfit, alreadyProfit.get()))
                .build();
            simUserPurseBos.add(sysUserPurseBo);
            //分润记录
            SimRenewProfitRecords simRenewProfitRecords = SimRenewProfitRecords.builder()
                .userId(sysUser.getUserId())
                .profit(NumberUtil.sub(taskProfit, alreadyProfit.get()))
                .build();
            arrayList.add(simRenewProfitRecords);
            return;
        }
        SimUserLevel simUserLevel = simUserLevelService.lambdaQuery().eq(SimUserLevel::getLevelNumber, sysUser.getLevel()).one();
        BigDecimal mul = NumberUtil.mul(taskProfit, simUserLevel.getLevelRate());
        //本级分润
        BigDecimal sub = NumberUtil.sub(mul, initProfit);
        alreadyProfit.set(NumberUtil.add(alreadyProfit.get(), sub));
        SimUserPurseBo sysUserPurseBo = SimUserPurseBo.builder()
            .userId(sysUser.getUserId())
            .type(SysUserProfitRecordsEnum.RENEW.getCode())
            .profit(sub)
            .build();
        simUserPurseBos.add(sysUserPurseBo);
        //分润记录
        SimRenewProfitRecords simRenewProfitRecords = SimRenewProfitRecords.builder()
            .userId(sysUser.getUserId())
            .profit(sub)
            .build();
        arrayList.add(simRenewProfitRecords);
        profitDifference(taskProfit, sysUser.getChildrenSysUser(), mul, alreadyProfit, simUserPurseBos, arrayList);
    }

    public String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
